package secrute;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.*;

/**
 * 加密算法工具类
 * Created by liyuan on 2018/4/11
 */
public class SecurityUtils {

    private SecurityUtils() {
    }

    private static final String SECRET = "wlw_pos_b2b";
    private static final String DEFAULT_CHARSET = "UTF-8";
    private static final String FINANCE_SECRET = "hWeXyhGiwyC1U1o3";


    public static String getFinanceSign(Map<String, Object> param) {
        //请求参数键值升序排序
        Collection<String> keySet = param.keySet();
        List<String> list = new ArrayList<>(keySet);
        Collections.sort(list);
        //拼参数字符串。
        StringBuilder signStr = new StringBuilder();
        for (String key : list) {
            String str = key + param.get(key);
            signStr.append(str);
        }
        //MD5签名并校验
        return md5Encode(signStr.toString() + FINANCE_SECRET);
    }

    /*
     * 常用的摘要算法有MD5,SHA1。摘要算法是一个不可逆过程，
     * 就是无论多大数据，经过算法运算后都是生成固定长度的数据,
     * 一般结果使用16进制进行显示。
     * MD5和SHA1的区别：MD5结果是128位摘要，SHa1是160位摘要。那么MD5的速度更快，而SHA1的强度更高。
     * 下面统一使用MD5算法进行说明，SHA1类似。
     * 主要用途有：验证消息完整性，安全访问认证，数据签名。
     * 消息完整性：由于每一份数据生成的MD5值不一样，因此发送数据时可以将数据和其MD5值一起发送，
     * 然后就可以用MD5验证数据是否丢失、修改。
     * 安全访问认证：这是使用了算法的不可逆性质，（就是无法从MD5值中恢复原数据）对账号登陆的密码进行MD5运算然后保存，
     * 这样可以保证除了用户之外，即使数据库管理人员都无法得知用户的密码。
     * 数字签名：这是结合非对称加密算法和CA证书的一种使用场景。
     * 一般破解方法：字典法，就是将常用密码生成MD5值字典，然后反向查找达到破解目的，因此建议使用强密码。
     */


    /**
     * 对文件进行MD5摘要
     *
     * @param path 文件路径
     * @return md5加密串
     */
    public static String getMD5(String path) {
        String md5 = "";
        File file = new File(path);
        try (FileInputStream is = new FileInputStream(file)) {
            FileChannel ch = is.getChannel();
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(byteBuffer);
            md5 = toHexString(md.digest());
        } catch (NoSuchAlgorithmException | IOException e) {
        }
        return md5;
    }

    /**
     * 对字符串进行MD5加密
     *
     * @param source 待加密字符串
     * @return md5加密串
     */
    public static String md5Encode(String source) {
        return md5Encode(source, DEFAULT_CHARSET, true);
    }


    /**
     * MD5不可逆简单加密:散列函数-基于HASH算法的编码
     *
     * @param source    原字符串
     * @param encoding  指定编码类型
     * @param uppercase 是否转为大写字符串
     * @return md5加密串
     */
    public static String md5Encode(String source, String encoding, boolean uppercase) {
        String result = null;
        try {
            result = source;
            // 获得MD5摘要对象
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 使用指定的字节数组更新摘要信息
            md.update(result.getBytes(encoding));
            // md.digest()获得16位长度
            result = byteArrayToHexString(md.digest());
        } catch (Exception e) {
        }
        return uppercase ? result.toUpperCase() : result;
    }

    private static char[] hex = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private static String toHexString(byte[] array) {
        char[] resultCharArray = new char[array.length * 2];
        int index = 0;
        for (byte b : array) {
            resultCharArray[index++] = hex[b >>> 4 & 0xf];
            resultCharArray[index++] = hex[b & 0xf];
        }
        return new String(resultCharArray);
    }

    public static String sha1(String source) {
        if ((source == null) || source.isEmpty()) {
            return source;
        }
        try {
            MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
            byte[] sha1Bytes = sha1.digest(source.getBytes());
            return toHexString(sha1Bytes);
        } catch (Exception e) {
            // do nothing
        }
        return source;
    }


    // 16进制的字符数组
    private static final String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};

    //byte转16进制
    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder();
        for (byte tem : bytes) {
            stringBuilder.append(byteToHexString(tem));
        }
        return stringBuilder.toString();
    }

    //16进制转byte[]
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }


    /**
     * 将二进制转换成16进制
     *
     * @param buf 字节数组
     */
    public static String parseByte2HexStr(byte[] buf) {
        StringBuilder sb = new StringBuilder();
        for (byte aBuf : buf) {
            String hex = Integer.toHexString(aBuf & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 将16进制转换为二进制
     *
     * @param hexStr 哈希字符串
     */
    public static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return new byte[0];
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }

    /**
     * 对文件进行SHA1摘要
     *
     * @param path 文件地址
     * @return 加密串
     */
    public static String getSHA1(String path) {
        String sha1 = "";
        File file = new File(path);
        try (FileInputStream is = new FileInputStream(file)) {
            FileChannel ch = is.getChannel();
            MappedByteBuffer byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            MessageDigest sha = MessageDigest.getInstance("SHA-1");
            sha.update(byteBuffer);
            sha1 = toHexString(sha.digest());
        } catch (NoSuchAlgorithmException | IOException e) {
           System.out.println("SHA1算法异常!" + e.getMessage());
        }
        return sha1;
    }


    /*
     * 对称加密算法
     * 对称加密算法只是为了区分非对称加密算法。其中鲜明的特点是对称加密是加密解密使用相同的密钥，
     * 而非对称加密加密和解密时使用的密钥不一样。对于大部分情况我们都使用对称加密，
     * 而对称加密的密钥交换时使用非对称加密，这有效保护密钥的安全。非对称加密加密和解密密钥不同，
     * 那么它的安全性是无疑最高的，但是它加密解密的速度很慢，不适合对大数据加密。而对称加密加密速度快，因此混合使用最好。
     * 常用的对称加密算法有：AES和DES.
     * DES：比较老的算法，一共有三个参数入口（原文，密钥，加密模式）。而3DES只是DES的一种模式，
     * 是以DES为基础更安全的变形，对数据进行了三次加密，也是被指定为AES的过渡算法。
     * AES:高级加密标准，新一代标准，加密速度更快，安全性更高（不用说优先选择）
     * AES密钥长度可以选择128位【16字节】，192位【24字节】和256位【32字节】密钥（其他不行，因此别乱设密码哦）
     * "AES/ECB/PKCS5Padding"在加密和解密时必须相同，可以直接写"AES",这样就是使用默认模式
     */
    private static SecretKeySpec getKey(int init, String algorithm) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance(algorithm);
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(SECRET.getBytes());
            kgen.init(init, secureRandom);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            //SecretKeySpec类是KeySpec接口的实现类,用于构建秘密密钥规范
            return new SecretKeySpec(enCodeFormat, algorithm);
        } catch (NoSuchAlgorithmException e) {
           System.out.println("获取密钥异常!" + e.getMessage());
        }
        return null;
    }


    private static final String HMAC_SHA1 = "HmacSHA1";

    /**
     * hmac算法
     *
     * @param encryptText 带加密字符串
     * @return 加密后字符串
     */
    public static String hMacSHA1Encrypt(String encryptText) {
        try {
            // 根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
            SecretKeySpec secretKey = new SecretKeySpec(SECRET.getBytes(DEFAULT_CHARSET), HMAC_SHA1);
            // 生成一个指定 Mac 算法 的 Mac 对象
            Mac mac = Mac.getInstance(HMAC_SHA1);
            // 用给定密钥初始化 Mac 对象
            mac.init(secretKey);
            return Base64Utils.encode(mac.doFinal(encryptText.getBytes(DEFAULT_CHARSET)));
        } catch (Exception e) {
           System.out.println("签名算法异常!" + e);
            return null;
        }
    }

    private static byte[] iv = {1, 9, 8, 9, 0, 6, 0, 4};
    private static byte[] iv2 = {1, 9, 8, 9, 0, 6, 0, 4, 1, 9, 9, 1, 0, 3, 0, 6};





}
